#include "mq_sensor.h"
#include <math.h>

/**
  * @brief  初始化MQ气体传感器
  * @param  mq: MQ传感器结构体指针
  * @param  hadc: ADC句柄指针
  * @param  adc_channel: ADC通道号
  * @param  vcc: 传感器供电电压(V)
  * @param  rl: 负载电阻值(kΩ)
  * @param  ratio_in_clean_air: 清洁空气中的Rs/Ro比值
  * @retval None
  */
void MQ_Init(MQ_Sensor* mq, ADC_HandleTypeDef* hadc, uint32_t adc_channel,
             float vcc, float rl, float ratio_in_clean_air) {
    mq->hadc = hadc;                    // 保存ADC句柄
    mq->adc_channel = adc_channel;      // 保存ADC通道
    mq->vcc = vcc;                      // 保存供电电压
    mq->rl = rl;                        // 保存负载电阻值
    mq->ratio_in_clean_air = ratio_in_clean_air;  // 保存清洁空气中的Rs/Ro比值
    mq->ro = 10.0f;                     // 设置Ro初始值，后续通过校准更新
}

// 对于多通道采集 CUBE 已经配置好 采集的规则序列这里破坏原来的
// 可以在主循环中采集好传入
static uint16_t MQ_ReadADC(MQ_Sensor* mq) {
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = mq->adc_channel;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
    HAL_ADC_ConfigChannel(mq->hadc, &sConfig);

    HAL_ADC_Start(mq->hadc);
    HAL_ADC_PollForConversion(mq->hadc, HAL_MAX_DELAY);
    uint16_t adc_value = HAL_ADC_GetValue(mq->hadc);
    HAL_ADC_Stop(mq->hadc);
    return adc_value;
}

float MQ_ReadRs(MQ_Sensor* mq) {
    uint16_t adc = MQ_ReadADC(mq);
    float v_out = ((float)adc / 4095.0f) * mq->vcc;
    float rs = ((mq->vcc - v_out) / v_out) * mq->rl;
    return rs;
}


float MQ_CalibrateRo(MQ_Sensor* mq, uint8_t sample_times) {
    float rs_sum = 0;
    for (uint8_t i = 0; i < sample_times; i++) {
        rs_sum += MQ_ReadRs(mq);
        HAL_Delay(200);
    }
    float avg_rs = rs_sum / sample_times;
    mq->ro = avg_rs / mq->ratio_in_clean_air;
    return mq->ro;
}

float MQ_GetRatio(MQ_Sensor* mq) {
    float rs = MQ_ReadRs(mq);
    return rs / mq->ro;
}

float MQ_GetPPM(MQ_Sensor* mq, float a, float b) {
    float ratio = MQ_GetRatio(mq);
    float ppm = a * powf(ratio, b);
    return ppm;
}

float MQ_ReadRs_ByADC(MQ_Sensor* mq, uint16_t adc_value) {
    float v_out = ((float)adc_value / 4095.0f) * mq->vcc;
    float rs = ((mq->vcc - v_out) / v_out) * mq->rl;
    return rs;
}

float MQ_CalibrateRo_ByADC(MQ_Sensor* mq, uint16_t* adc_values, uint8_t sample_times) {
    float rs_sum = 0;
    for (uint8_t i = 0; i < sample_times; i++) {
        rs_sum += MQ_ReadRs_ByADC(mq, adc_values[i]);
    }
    float avg_rs = rs_sum / sample_times;
    mq->ro = avg_rs / mq->ratio_in_clean_air;
    return mq->ro;
}

float MQ_GetRatio_ByADC(MQ_Sensor* mq, uint16_t adc_value) {
    float rs = MQ_ReadRs_ByADC(mq, adc_value);
    return rs / mq->ro;
}

float MQ_GetPPM_ByADC(MQ_Sensor* mq, uint16_t adc_value, float a, float b) {
    float ratio = MQ_GetRatio_ByADC(mq, adc_value);
    float ppm = a * powf(ratio, b);
    return ppm;
}
